package edu.zsc.logic;


import edu.zsc.ElevatorConfig;

import java.util.Map;

public class Algorithm {

    private static Algorithm INSTANT = new Algorithm();
    private Callback callback;

    int[][][] myMission;//三维任务列表

    /**
     * 算法核心成员属性
     */
    //楼宇电梯固定属性
    private int F;//楼层数
    private int N;//电梯数
    private int StanNum;//荷载人数
    private float v;//平均运行速度
    private float H;//楼层高度
    private float T;//平均停靠时间

    //计算评分所需的内部变量
    double[] Score;//每台电梯的评分
    int[] CountNum;//每台电梯的累积人数

    // 内测属性
    private static final double w1 = 0.6, w2 = 0.4, w3 = 0;//三个时间的权重系数
    /**算法核心成员属性*/

    private Algorithm() {
        this.callback = new NullCallback();

        //配置电梯参数
        this.F = ElevatorConfig.FLOOR_COUNT;// 电梯最高楼层
        this.N = ElevatorConfig.ELEVATOR_COUNT;// 电梯的数量
        this.StanNum = ElevatorConfig.STAN_COUNT;// 荷载人数
        this.v = (float) ElevatorConfig.SPEED;// 电梯运行的平均速度
        this.H = (float) ElevatorConfig.EACH_FLOOR_HEIGHT;//楼层高度
        this.T = (float) ElevatorConfig.STOP_TIME;//平均停靠时间
        this.Score = new double[N];
        this.CountNum = new int[N];
        for (int i = 0; i < N; i++)//初始化评分数组
        {
            this.Score[i] = 999;
        }
        for (int i = 0; i < N; i++)//初始化电梯人数数组
        {
            this.CountNum[i] = 0;
        }

        // 初始化三维任务列表
        this.myMission = new int[N][F][4];
        InitMyMission();
    }

    // 初始化三维任务列表
    private void InitMyMission() {
        for (int i = 0; i < this.N; i++) {
            for (int j = 0; j < this.F; j++) {
                for (int k = 0; k < 4; k++) {
                    this.myMission[i][j][k] = 0;
                }
            }
        }
    }


    public static Algorithm getInstance() {
        return INSTANT;
    }

    public void setCallback(Callback callback) {
        this.callback = callback;
    }

    public interface Callback {

        /**
         * 用于完成Command计算后的回调
         *
         * @param target 目标楼层
         */
        void onCommandCompleted(int target);
    }


    /**
     * key : 各电梯号
     * value ： 电梯号对应的电梯的上一次状态
     */
    private Map<Character, InputMessage> prevMessage;


    /**
     * 当电梯状态发生改变的时候被调用
     *
     * @param inputMessage 电梯状态值
     */
    public void handleElevatorStateChange(InputMessage inputMessage) {

    }




    /**算法核心层 */
    /**
     * 根据乘客信息和电梯状态计算最优梯号
     * @param SF 乘客起始楼层
     * @param DF 乘客目标楼层
     * @param Num 乘客人数
     * @param ED 电梯方向
     * @param EF 电梯楼层
     * @param LF 电梯最近截梯楼层
     * @param Mission 电梯任务列表
     * @return 电梯号
     */
    public int FindBestElevator(int SF, int DF, int Num, int[] ED, int[] EF, int[] LF, int[][][] Mission) {
        int PD = FindDirectionForPassenger(SF, DF);// 乘客乘梯方向
        double Twait = 0;//等待时间
        double Tride = 0;//乘梯时间
        int Ed;//电梯方向
        int Ef;//电梯所在楼层
        int Lf;//电梯最近截梯楼层
        int[] CountNum = CalCountNumber(PD, SF, DF, Mission);
        int i = 0;
        int flag = 0;
        int goal = -1;//评分最高的电梯 若为-1则全部电梯超载
        for (; i < N; i++) {
            Ed = ED[i];//0静止 1向上 2向下 3静止向上 4静止向下
            Ef = EF[i];
            Lf = LF[i];
            if (CountNum[i] >= StanNum) continue;
            if ((Ed == 3 && PD == 2) || (Ed == 3 && Ef != SF))//电梯正在上下客，但电梯方向与乘客方向反向或者不在乘客呼梯楼层
            {
                Ed = 1;
            }
            if ((Ed == 4 && PD == 1) || (Ed == 4 && Ef != SF))//电梯正在上下客，但电梯方向与乘客方向反向或者不在乘客呼梯楼层
            {
                Ed = 1;
            }
            if (Ed == 0)//电梯无任务静止
            {
                Twait = abs(Ef - SF) * H / v;
                Tride = abs(SF - DF) * H / v;
            } else if ((Ed == 3 && PD == 1 && Ef == SF) || (Ed == 4 && PD == 3 && Ef == SF))//电梯正在上下乘客 电梯与乘客楼层和方向均相同
            {
                Twait = 0;
                Tride = abs(SF - DF) * H / v + FindBetweenInArray1(i, SF, DF, Mission) * T;
            } else if (PD == 1 && Ed == 1 && Ef < SF && SF >= Lf)//乘客方向向上 电梯方向向上 电梯楼层小于呼梯楼层 乘客楼层不小于截梯楼层
            {
                Twait = abs(Ef - SF) * H / v + FindBetweenInArray2(i, Ef, SF, Mission) * T;
                Tride = abs(SF - DF) * H / v + FindBetweenInArray1(i, SF, DF, Mission) * T;
            } else if ((PD == 1 && Ed == 1 && Ef < SF && SF < Lf))
            //乘客方向向上 电梯方向向上 电梯楼层小于呼梯楼层 乘客楼层小于截梯楼层
            {
                int Max = FindMaxFloor(i, Ef, Mission);
                int Min = FindMinFloor(i, Max, Mission);
                Twait = (abs(Ef - Max) + abs(Max - Min) + abs(Min - SF)) * H / v + (FindBetweenInArray3(i, Ef, Max, Mission) +
                        FindBetweenInArray3(i, Max, Min, Mission) + FindBetweenInArray3(i, Min, SF, Mission)) * T;
                Tride = abs(SF - DF) * H / v;
            } else if ((PD == 1 && Ed == 1 && Ef >= SF))//乘客方向向上 电梯方向向上 电梯楼层大于呼梯楼层
            {
                int Max = FindMaxFloor(i, Ef, Mission);
                int Min = FindMinFloor(i, Max, Mission);
                Twait = (abs(Ef - Max) + abs(Max - Min) + abs(Min - SF)) * H / v + (FindBetweenInArray3(i, Ef, Max, Mission) +
                        FindBetweenInArray3(i, Max, Min, Mission) + FindBetweenInArray3(i, Min, SF, Mission)) * T;
                Tride = abs(SF - DF) * H / v + FindBetweenInArray2(i, SF, Ef, Mission) * T;
            } else if (PD == 1 && Ed == 2)//乘客方向向上 电梯方向向下
            {
                int Min = FindMinFloor(i, Ef, Mission);
                Twait = (abs(Ef - Min) + abs(Min - SF)) * H / v + (FindBetweenInArray3(i, Ef, Min, Mission) + FindBetweenInArray3(i, Min, SF, Mission)) * T;
                Tride = abs(SF - DF) * H / v + FindBetweenInArray1(i, SF, DF, Mission) * T;
            }
//乘客方向向下
            else if (PD == 2 && Ed == 2 && Ef > SF && SF <= Lf)//乘客方向向下 电梯方向向下 电梯楼层大于呼梯楼层 乘客楼层不大于截梯楼层
            {
                Twait = abs(Ef - SF) * H / v + FindBetweenInArray2(i, Ef, SF, Mission) * T;
                Tride = abs(SF - DF) * H / v + FindBetweenInArray1(i, SF, DF, Mission) * T;
            } else if ((PD == 2 && Ed == 2 && Ef > SF && SF > Lf))
            //乘客方向向下 电梯方向向下 电梯楼层大于呼梯楼层 乘客楼层大于截梯楼层
            {
                int Min = FindMinFloor(i, Ef, Mission);
                int Max = FindMaxFloor(i, Min, Mission);
                Twait = (abs(Ef - Min) + abs(Min - Max) + abs(Max - SF)) * H / v + (FindBetweenInArray3(i, Ef, Min, Mission) +
                        FindBetweenInArray3(i, Min, Max, Mission) + FindBetweenInArray3(i, Max, SF, Mission)) * T;
                Tride = abs(SF - DF) * H / v;
            } else if ((PD == 2 && Ed == 2 && Ef >= SF))//乘客方向向下 电梯方向向下 电梯楼层小于呼梯楼层
            {
                int Min = FindMinFloor(i, Ef, Mission);
                int Max = FindMaxFloor(i, Min, Mission);
                Twait = (abs(Ef - Min) + abs(Min - Max) + abs(Max - SF)) * H / v + (FindBetweenInArray3(i, Ef, Min, Mission) +
                        FindBetweenInArray3(i, Min, Max, Mission) + FindBetweenInArray3(i, Max, SF, Mission)) * T;
                Tride = abs(SF - DF) * H / v + FindBetweenInArray2(i, SF, Ef, Mission) * T;
            } else if (PD == 2 && Ed == 1)//乘客方向向下 电梯方向向上
            {
                int Max = FindMinFloor(i, Ef, Mission);
                Twait = (abs(Ef - Max) + abs(Max - SF)) * H / v + (FindBetweenInArray3(i, Ef, Max, Mission) + FindBetweenInArray3(i, Max, SF, Mission)) * T;
                Tride = abs(SF - DF) * H / v + FindBetweenInArray1(i, SF, DF, Mission) * T;
            }
            Score[i] = w1 * Twait + w2 * Tride;
            System.out.println(Score[i]);
            flag = 1;
        }
        if (flag == 1) {
            goal = FindMinScoreNumber(Score);
        }
        return goal;
    }

    private static double abs(int i) {
        return Math.abs(i);
    }

    private int FindMinScoreNumber(double[] Score) {
        int Min = 0;
        for (int i = 1; i < N; i++) {
            if (Score[i] < Score[Min]) {
                Min = i;
            }
        }
        return Min;
    }

    //根据乘客起始楼层和目的楼层判断方向 1向上 2向下
    private int FindDirectionForPassenger(int PassengerCurrentFloor, int PassengerDestinationFloor) {
        int direction;
        if (PassengerCurrentFloor - PassengerDestinationFloor < 0) {
            direction = 1;
        } else {
            direction = 2;
        }
        return direction;
    }

    //根据任务列表计算每部电梯在完成此任务时的累积人数
    private int[] CalCountNumber(int PD, int FC, int FD, int[][][] Mission) {
        int[] CountNumber = new int[N];
        if (PD == 1) {
            for (int j = 0; j < N; ++j) {
                for (int k = 0; k <= FC; ++k) {
                    if (Mission[j][k][0] != 0) {
                        CountNumber[j] += Mission[j][k][0];
                    }

                    if (Mission[j][k][2] != 0) {
                        CountNumber[j] -= Mission[j][k][2];
                    }
                }
                for (int k = FC + 1; k < FD; k++) {
                    if (Mission[j][k][0] != 0) {
                        CountNumber[j] += Mission[j][k][0];
                    }
                }
            }
        } else {
            for (int j = 0; j < N; ++j) {
                for (int k = F - 1; k >= FC; --k) {
                    if (Mission[j][k][1] != 0) {
                        CountNumber[j] += Mission[j][k][1];
                    }

                    if (Mission[j][k][3] != 0) {
                        CountNumber[j] -= Mission[j][k][3];
                    }
                }
                for (int k = FC - 1; k > FD; --k) {
                    if (Mission[j][k][1] != 0) {
                        CountNumber[j] += Mission[j][k][1];
                    }
                }
            }
        }
        return CountNumber;
    }

    private int FindMaxFloor(int i, int floor, int[][][] Mission) {
        int Max = floor;
        for (int j = floor; j < F; j++) {
            if (Mission[i][j][0] != 0 || Mission[i][j][2] != 0 || Mission[i][j][1] != 0 || Mission[i][j][3] != 0) {
                if (j > Max) {
                    Max = j;
                }
            }
        }
        return Max;
    }

    private int FindMinFloor(int i, int floor, int[][][] Mission) {
        int Min = floor;
        for (int j = floor - 1; j >= 0; j--) {
            if (Mission[i][j][1] != 0 || Mission[i][j][3] != 0 || Mission[i][j][0] != 0 || Mission[i][j][2] != 0) {
                if (j < Min) {
                    Min = j;
                }
            }
        }
        return Min;
    }

    private int FindBetweenInArray1(int i, int floor1, int floor2, int[][][] Mission) {//开区间
        int direction = floor1 - floor2;
        int stop_floor = 0;
        if (direction < 0) {//向上
            for (int j = floor1 + 1; j < floor2; j++) {
                if (Mission[i][j][0] != 0 || Mission[i][j][2] != 0) {
                    stop_floor++;
                }
            }

        } else {//向下

            for (int j = floor2 + 1; j < floor1; j++) {
                if (Mission[i][j][1] != 0 || Mission[i][j][3] != 0) {
                    stop_floor++;
                }
            }
        }
        return stop_floor;
    }

    private int FindBetweenInArray3(int i, int floor1, int floor2, int[][][] FloorCount) {//左闭右开
        int direction = floor1 - floor2;
        int stop_floor = 0;
        if (direction < 0) {//向上
            for (int j = floor1; j < floor2; j++) {
                if (FloorCount[i][j][0] != 0 || FloorCount[i][j][2] != 0) {
                    stop_floor++;
                }
            }
        } else {//向下
            for (int j = floor2 + 1; j <= floor1; j++) {
                if (FloorCount[i][j][1] != 0 || FloorCount[i][j][3] != 0) {
                    stop_floor++;
                }
            }
        }
        return stop_floor;
    }

    private int FindBetweenInArray2(int i, int floor1, int floor2, int[][][] FloorCount) {//左开右闭
        int direction = floor1 - floor2;
        int stop_floor = 0;
        if (direction < 0) {//向上
            for (int j = floor1 + 1; j <= floor2; j++) {
                if (FloorCount[i][j][0] != 0 || FloorCount[i][j][2] != 0) {
                    stop_floor++;
                }
            }

        } else {//向下

            for (int j = floor2; j < floor1; j++) {
                if (FloorCount[i][j][1] != 0 || FloorCount[i][j][3] != 0) {
                    stop_floor++;
                }
            }
        }
        return stop_floor;
    }

}
